/*
 * @Author: yxfan
 * @Date: 2024-06-21 13:39:55
 * @LastEditTime: 2024-10-10 18:21:50
 * @FilePath: /umi-demo/src/pages/demo/Diff/Render.jsx
 * @Description:
 */
import { useState, useEffect, useRef, Fragment } from 'react';
import { Radio, Layout } from 'antd';
const { Content } = Layout;
import s from './index.css';

import cx from 'classnames';
const BLOCK_LENGTH = 1;
const SHOW_TYPE = {
    UNIFIED: 0,
    SPLITED: 1,
};

export default (props) => {
    const { diffArr } = props;
    const [lineGroup, setLineGroup] = useState([]);
    const [showType, setShowType] = useState(SHOW_TYPE.SPLITED);

    useEffect(() => {
        flashContent();
    }, [diffArr]);

    const flashContent = (newArr) => {
        if (typeof (newArr || diffArr) === 'string') return;
        const initLineGroup = (newArr || diffArr).map(
            (item, index, originArr) => {
                let added, removed, value, count;

                added = item.added;
                removed = item.removed;
                value = item.value;
                count = item.count;
                //  以\n为分隔符，将value分割成以行划分的代码
                const strArr = value?.split('\n').filter((item) => item) || [];
                //  获得当前数据块的类型+标识新增 -表示移除 空格表示相同的内容
                const type = (added && '+') || (removed && '-') || ' ';
                //  定义代码块的内部结构，分为头部，尾部和中间的隐藏部分
                let head, hidden, tail;
                //  如果是增加或者减少的代码块，头部填入内容，尾部和隐藏区域都为空
                if (type !== ' ') {
                    hidden = [];
                    tail = [];
                    head = strArr;
                } else {
                    const strLength = strArr.length;
                    if (strLength <= BLOCK_LENGTH * 2) {
                        hidden = [];
                        tail = [];
                        head = strArr;
                    } else {
                        head = strArr.slice(0, BLOCK_LENGTH);
                        hidden = strArr.slice(
                            BLOCK_LENGTH,
                            strLength - BLOCK_LENGTH,
                        );
                        tail = strArr.slice(strLength - BLOCK_LENGTH);
                    }
                }
                return {
                    type,
                    count,
                    content: {
                        hidden,
                        head,
                        tail,
                    },
                };
            },
        );
        let lStartNum = 1;
        let rStartNum = 1;
        // console.log('initLineGroup', JSON.parse(JSON.stringify(initLineGroup)));

        initLineGroup.forEach((item) => {
            // debugger
            const { type, count } = item;
            item.leftPos = lStartNum;
            item.rightPos = rStartNum;
            lStartNum += type === '+' ? 0 : count;
            rStartNum += type === '-' ? 0 : count;
        });
        console.log('initLineGroup', initLineGroup);
        setLineGroup(initLineGroup);
    };
    console.log('lineGroup', lineGroup);

    const getUnifiedRenderContent = () => {
        return lineGroup.map((item, index) => {
            const {
                type,
                content: { hidden },
            } = item;
            const isNormal = type === ' ';
            return (
                <div key={index}>
                    {paintCode(item)}
                    {(hidden.length &&
                        isNormal &&
                        getHiddenBtn(hidden, index)) ||
                        null}
                    {paintCode(item, false)}
                </div>
            );
        });
    };

    const getLineNum = (number) => {
        return ('     ' + number).slice(-5);
    };

    const paintCode = (item, isHead = true) => {
        const {
            type,
            content: { head, tail, hidden },
            leftPos,
            rightPos,
        } = item;
        const isNormal = type === ' ';
        const cls = cx(
            s.normal,
            type === '+' ? s.add : '',
            type === '-' ? s.removed : '',
        );
        const space = '     ';
        return (isHead ? head : tail).map((sitem, sindex) => {
            let posMark = '';
            if (isNormal) {
                const shift = isHead ? 0 : head.length + hidden.length;
                posMark =
                    (space + (leftPos + shift + sindex)).slice(-5) +
                    (space + (rightPos + shift + sindex)).slice(-5);
            } else {
                posMark =
                    type === '-'
                        ? getLineNum(leftPos + sindex) + space
                        : space + getLineNum(rightPos + sindex);
            }
            return (
                <div key={(isHead ? 'h-' : 't-') + sindex} className={cls}>
                    <pre className={cx(s.pre, s.line)}>{posMark}</pre>
                    <div className={s.outerPre}>
                        <div className={s.splitCon}>
                            <div className={s.spanWidth}>
                                {' ' + type + ' '}
                            </div>
                            {getPaddingContent(sitem, true)}
                        </div>
                    </div>
                </div>
            );
        });
    };

    //  获取split下的页码node
    const getLNPadding = (origin) => {
        const item = ('     ' + origin).slice(-5);
        return <div className={cx(s.splitLN, 'num')}>{item}</div>;
    };

    function isSplit() {
        return showType === SHOW_TYPE.SPLITED;
    }

    const openBlock = (type, index) => {
        const copyOfLG = lineGroup.slice();
        const targetGroup = copyOfLG[index];
        const { head, tail, hidden } = targetGroup.content;
        if (type === 'head') {
            targetGroup.content.head = head.concat(
                hidden.slice(0, BLOCK_LENGTH),
            );
            targetGroup.content.hidden = hidden.slice(BLOCK_LENGTH);
        } else if (type === 'tail') {
            const hLenght = hidden.length;
            targetGroup.content.tail = hidden
                .slice(hLenght - BLOCK_LENGTH)
                .concat(tail);
            targetGroup.content.hidden = hidden.slice(
                0,
                hLenght - BLOCK_LENGTH,
            );
        } else {
            targetGroup.content.head = head.concat(hidden);
            targetGroup.content.hidden = [];
        }
        copyOfLG[index] = targetGroup;
        setLineGroup(copyOfLG);
    };
    const getHiddenBtn = (hidden, index) => {
        const isSingle = hidden.length < BLOCK_LENGTH * 2;
        return (
            <div key="collapse" className={s.cutWrapper}>
                <div className={cx(s.colLeft, isSplit() ? s.splitWidth : '')}>
                    {isSingle ? (
                        <div
                            className={s.arrow}
                            onClick={() => openBlock('all', index)}
                        >
                            <svg
                                className={s.octicon}
                                viewBox="0 0 16 16"
                                version="1.1"
                                width="16"
                                height="16"
                                aria-hidden="true"
                            >
                                <path
                                    fillRule="evenodd"
                                    d="M8.177.677l2.896 2.896a.25.25 0 01-.177.427H8.75v1.25a.75.75 0 01-1.5 0V4H5.104a.25.25 0 01-.177-.427L7.823.677a.25.25 0 01.354 0zM7.25 10.75a.75.75 0 011.5 0V12h2.146a.25.25 0 01.177.427l-2.896 2.896a.25.25 0 01-.354 0l-2.896-2.896A.25.25 0 015.104 12H7.25v-1.25zm-5-2a.75.75 0 000-1.5h-.5a.75.75 0 000 1.5h.5zM6 8a.75.75 0 01-.75.75h-.5a.75.75 0 010-1.5h.5A.75.75 0 016 8zm2.25.75a.75.75 0 000-1.5h-.5a.75.75 0 000 1.5h.5zM12 8a.75.75 0 01-.75.75h-.5a.75.75 0 010-1.5h.5A.75.75 0 0112 8zm2.25.75a.75.75 0 000-1.5h-.5a.75.75 0 000 1.5h.5z"
                                ></path>
                            </svg>
                        </div>
                    ) : (
                        <Fragment>
                            <div
                                className={s.arrow}
                                onClick={() => openBlock('head', index)}
                            >
                                <svg
                                    className={s.octicon}
                                    viewBox="0 0 16 16"
                                    version="1.1"
                                    width="16"
                                    height="16"
                                    aria-hidden="true"
                                >
                                    <path
                                        fillRule="evenodd"
                                        d="M8.177 14.323l2.896-2.896a.25.25 0 00-.177-.427H8.75V7.764a.75.75 0 10-1.5 0V11H5.104a.25.25 0 00-.177.427l2.896 2.896a.25.25 0 00.354 0zM2.25 5a.75.75 0 000-1.5h-.5a.75.75 0 000 1.5h.5zM6 4.25a.75.75 0 01-.75.75h-.5a.75.75 0 010-1.5h.5a.75.75 0 01.75.75zM8.25 5a.75.75 0 000-1.5h-.5a.75.75 0 000 1.5h.5zM12 4.25a.75.75 0 01-.75.75h-.5a.75.75 0 010-1.5h.5a.75.75 0 01.75.75zm2.25.75a.75.75 0 000-1.5h-.5a.75.75 0 000 1.5h.5z"
                                    ></path>
                                </svg>
                            </div>
                            <div
                                className={s.arrow}
                                onClick={() => openBlock('tail', index)}
                            >
                                <svg
                                    className={s.octicon}
                                    viewBox="0 0 16 16"
                                    version="1.1"
                                    width="16"
                                    height="16"
                                    aria-hidden="true"
                                >
                                    <path
                                        fillRule="evenodd"
                                        d="M7.823 1.677L4.927 4.573A.25.25 0 005.104 5H7.25v3.236a.75.75 0 101.5 0V5h2.146a.25.25 0 00.177-.427L8.177 1.677a.25.25 0 00-.354 0zM13.75 11a.75.75 0 000 1.5h.5a.75.75 0 000-1.5h-.5zm-3.75.75a.75.75 0 01.75-.75h.5a.75.75 0 010 1.5h-.5a.75.75 0 01-.75-.75zM7.75 11a.75.75 0 000 1.5h.5a.75.75 0 000-1.5h-.5zM4 11.75a.75.75 0 01.75-.75h.5a.75.75 0 010 1.5h-.5a.75.75 0 01-.75-.75zM1.75 11a.75.75 0 000 1.5h.5a.75.75 0 000-1.5h-.5z"
                                    ></path>
                                </svg>
                            </div>
                        </Fragment>
                    )}
                </div>
                <div
                    className={cx(
                        s.collRight,
                        isSplit() ? s.collRightSplit : '',
                    )}
                >
                    <div
                        className={cx(
                            s.colRContent,
                            isSingle ? '' : s.cRHeight,
                        )}
                    >{`当前隐藏内容:${hidden.length}行`}</div>
                </div>
            </div>
        );
    };
    const getSplitCode = (targetBlock, isHead = true) => {
        const {
            type,
            content: { head, hidden, tail },
            leftPos,
            rightPos,
        } = targetBlock;
        return (isHead ? head : tail).map((item, index) => {
            const shift = isHead ? 0 : head.length + hidden.length;
            return (
                <div key={(isHead ? 'h-' : 't-') + index}>
                    <div className={cx(s.iBlock, s.lBorder)}>
                        {getLNPadding(leftPos + shift + index)}
                        {getPaddingContent('    ' + item)}
                    </div>
                    <div className={s.iBlock}>
                        {getLNPadding(rightPos + shift + index)}
                        {getPaddingContent('    ' + item)}
                    </div>
                </div>
            );
        });
    };

    const getCombinePart = (leftPart = {}, rightPart = {}) => {
        const {
            type: lType,
            content: lContent,
            leftPos: lLeftPos,
            rightPos: lRightPos,
        } = leftPart;
        const {
            type: rType,
            content: rContent,
            leftPos: rLeftPos,
            rightPos: rRightPos,
        } = rightPart;
        const lArr = lContent?.head || [];
        const rArr = rContent?.head || [];
        const lClass = lType === '+' ? s.add : s.removed;
        const rClass = rType === '+' ? s.add : s.removed;
        // debugger;
        return (
            <Fragment>
                <div className={cx(s.iBlock, s.lBorder, 'left')}>
                    {lArr.map((item, index) => {
                        return (
                            <div className={cx(s.prBlock, lClass)} key={index}>
                                {getLNPadding(lLeftPos + index)}
                                {getPaddingContent('-  ' + item)}
                            </div>
                        );
                    })}
                </div>
                <div
                    className={cx(
                        s.iBlock,
                        lArr.length ? '' : s.rBorder,
                        'righ',
                    )}
                >
                    {rArr.map((item, index) => {
                        return (
                            <div className={cx(s.prBlock, rClass)} key={index}>
                                {getLNPadding(rRightPos + index)}
                                {getPaddingContent('+  ' + item)}
                            </div>
                        );
                    })}
                </div>
            </Fragment>
        );
    };

    //  获取split下的内容node
    const getPaddingContent = (item) => {
        return <div className={cx(s.splitCon)}>{item}</div>;
    };

    const getSplitContent = () => {
        const length = lineGroup.length;
        const contentList = [];
        for (let i = 0; i < length; i++) {
            const targetBlock = lineGroup[i];
            const {
                type,
                content: { hidden },
            } = targetBlock;
            if (type === ' ') {
                contentList.push(
                    <div key={i}>
                        {getSplitCode(targetBlock)}
                        {(hidden.length && getHiddenBtn(hidden, i)) || null}
                        {getSplitCode(targetBlock, false)}
                    </div>,
                );
            } else if (type === '-') {
                const nextTarget = lineGroup[i + 1] || { content: {} };
                const nextIsPlus = nextTarget.type === '+';
                contentList.push(
                    <div key={i} className="row-item">
                        {getCombinePart(
                            targetBlock,
                            nextIsPlus ? nextTarget : {},
                        )}
                    </div>,
                );
                nextIsPlus ? (i = i + 1) : void 0;
            } else if (type === '+') {
                contentList.push(
                    <div key={i}>{getCombinePart({}, targetBlock)}</div>,
                );
            }
        }
        return <div>{contentList}</div>;
    };

    const handleShowTypeChange = (e) => {
        setShowType(e.target.value);
    };

    return (
        <Fragment>
            <div className={s.radioGroup}>
                <Radio.Group
                    value={showType}
                    size="small"
                    onChange={handleShowTypeChange}
                >
                    <Radio.Button value={SHOW_TYPE.UNIFIED}>
                        Unified
                    </Radio.Button>
                    <Radio.Button value={SHOW_TYPE.SPLITED}>Split</Radio.Button>
                </Radio.Group>
            </div>

            <Content className={s.content}>
                <div className={s.color}>
                    {isSplit() ? getSplitContent() : getUnifiedRenderContent()}
                </div>
            </Content>
        </Fragment>
    );
};
